package com.hollysys.usersync.service.impl;

import com.hollysys.usersync.entity.Response;
import com.hollysys.usersync.entity.SyncResult;
import com.hollysys.usersync.entity.platform.RelUser;
import com.hollysys.usersync.entity.platform.User;
import com.hollysys.usersync.feign.AuthServiceInvoker;
import com.hollysys.usersync.interceptor.AppToken;
import com.hollysys.usersync.service.SyncOrgService;
import com.hollysys.usersync.service.SyncPostService;
import com.hollysys.usersync.service.SyncUserService;
import com.hollysys.usersync.utils.ExecuteUtil;
import com.hollysys.usersync.utils.MapUtil;
import com.hollysys.usersync.utils.SpringBeanUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 用户信息同步服务 - 实现类
 *
 * @author Jeremy Yim
 */
@Service
@Log4j2
public class SyncUserServiceImpl implements SyncUserService {

    @Resource
    private AuthServiceInvoker authService;

    @Resource
    private SyncOrgService syncOrgService;

    @Resource
    private SyncPostService syncPostService;

    /**
     * 默认用户密码
     */
    @Value("${hollysys.default.password}")
    private String userDefaultPassword;

    /**
     * 默认用户角色
     */
    @Value("${hollysys.default.roleId}")
    private String userDefaultRole;

    /**
     * 默认组织ID
     */
    @Value("${hollysys.default.rootOrgId}")
    private String defaultOrgId;


    /**
     * 线程池
     */
    private final ExecutorService executorService = new ScheduledThreadPoolExecutor(10,
            new BasicThreadFactory.Builder().namingPattern("sync-schedule-pool-%d").daemon(true).build());

    /**
     * 获得用户信息字典
     *
     * @param key    字典键
     * @param value  字典值
     * @param filter 过滤条件
     * @return 用户信息字典
     */
    @Override
    public <R, V> Map<R, V> getUserMap(@NotNull Function<User, R> key, @NotNull Function<User, V> value,
                                       @Nullable Predicate<User> filter) {
        // 获取用户全量列表
        Response<List<User>> userListResponse = authService.getUser(AppToken.getToken());
        if (userListResponse.getStatus() != 0) {
            log.error("用户列表查询失败" + userListResponse.getMessage());
            return new HashMap<>(0);
        }
        // 筛选分类
        filter = Objects.isNull(filter) ? u -> true : filter;
        return userListResponse.getData().stream()
                .filter(filter)
                .collect(Collectors.toMap(key, value));
    }

    /**
     * 新增用户
     *
     * @param userList 用户实体列表
     * @return 新增失败的用户及原因
     */
    @Override
    public List<SyncResult> addUsers(List<User> userList) {

        // 转换组织&岗位code -> ID
        Set<String> orgCodeList = new HashSet<>();
        Set<String> postCodeList = new HashSet<>();
        userList.parallelStream().forEach(user -> {
            orgCodeList.add(user.getDepartmentCode());
            postCodeList.addAll(Optional.ofNullable(user.getPosts()).orElse(new ArrayList<>()));
        });
        // 组织为字典
        Map<String, String> orgIdMap = syncOrgService.convertOrgCode2Id(new ArrayList<>(orgCodeList));
        Map<String, String> postIdMap = syncPostService.convertPostCode2Id(new ArrayList<>(postCodeList));
        // 新增用户
        List<ExecuteUtil.AsyncResult<User, Response<String>>> resultList = ExecuteUtil.batchAsyncCall(userList, executorService,
                (user) -> {
                    user.setPosts(MapUtil.getValueByKey(user.getPosts(), postIdMap).orElse(null));
                    user.setOrgs(Collections.singletonList(orgIdMap.getOrDefault(user.getDepartmentCode(), defaultOrgId)));
                    // 设置密码为默认值
                    user.setPassword(DigestUtils.md5DigestAsHex(
                            Optional.ofNullable(user.getPassword()).orElse(userDefaultPassword).getBytes())
                    );
                    // 设置用户类型
                    user.setUserType(1);
                    // 设置默认角色
                    user.setMainRole(userDefaultRole);
                    user.setRoles(Collections.singletonList(userDefaultRole));
                    Response<String> response = authService.addUser(user, AppToken.getToken());
                    // 平台的用户操作接口并不支持用户和岗位关联，因此需要单独处理
                    if (response.getStatus() != 0 || ObjectUtils.isEmpty(user.getPosts())) {
                        return response;
                    }
                    user.getPosts().forEach(postId -> {
                        RelUser rel = RelUser.builder().users(Collections.singletonList(response.getData())).id(postId).build();
                        Response<String> relResponse = authService.addPostUser(rel, AppToken.getToken());
                        if (relResponse.getStatus() != 0) {
                            response.setMessage(response.getMessage().concat(" " + relResponse.getMessage()));
                        }
                    });
                    return response;
                });

        return resultList.stream().map(result -> SyncResult.builder()
                .status(result.getResult() && result.getValue().getStatus() == 0)
                .code(result.getRawParam().getAccount())
                .message(result.getResult() ? result.getValue().getMessage() : result.getExceptionMessage())
                .build()
        ).collect(Collectors.toList());

    }

    /**
     * 修改用户信息
     *
     * @param userList 用户实体列表
     * @return 用户同步状态
     */
    @Override
    public List<SyncResult> updateUsers(List<User> userList) {
        // 转换组织&岗位
        Set<String> orgCodeList = new HashSet<>();
        Set<String> postCodeList = new HashSet<>();
        userList.parallelStream().forEach(user -> {
            orgCodeList.add(user.getDepartmentCode());
            postCodeList.addAll(Optional.ofNullable(user.getPosts()).orElse(new ArrayList<>()));
        });
        // 组织为字典
        Map<String, String> orgIdMap = syncOrgService.convertOrgCode2Id(new ArrayList<>(orgCodeList));
        Map<String, String> postIdMap = syncPostService.convertPostCode2Id(new ArrayList<>(postCodeList));
        Map<String, User> userMap = this.getUserMap(User::getAccount, user -> user, null);

        // 更新用户
        List<ExecuteUtil.AsyncResult<User, Response<String>>> resultList = ExecuteUtil.batchAsyncCall(userList, executorService,
                (user) -> {
                    User userEntity = Optional.ofNullable(userMap.get(user.getAccount()))
                            .orElseThrow(() -> new RuntimeException("该用户不存在！"));
                    SpringBeanUtil.copyPropertiesIgnoreNull(user, userEntity);
                    userEntity.setMainRole(null);
                    userEntity.setRoles(null);
                    userEntity.setPassword(null);
                    // 设置用户类型
                    userEntity.setUserType(1);
                    userEntity.setPosts(MapUtil.getValueByKey(user.getPosts(), postIdMap).orElse(null));
                    userEntity.setOrgs(Collections.singletonList(orgIdMap.getOrDefault(user.getDepartmentCode(), defaultOrgId)));
                    Response<String> response = authService.updateUser(userEntity, AppToken.getToken());
                    // 平台的用户操作接口并不支持用户和岗位关联，因此需要单独处理
                    if (response.getStatus() != 0 || ObjectUtils.isEmpty(userEntity.getPosts())) {
                        return response;
                    }
                    userEntity.getPosts().forEach(postId -> {
                        RelUser rel = RelUser.builder().users(Collections.singletonList(response.getData())).id(postId).build();
                        Response<String> relResponse = authService.addPostUser(rel, AppToken.getToken());
                        if (relResponse.getStatus() != 0) {
                            response.setMessage(response.getMessage().concat(" " + relResponse.getMessage()));
                        }
                    });
                    return response;
                });
        return resultList.stream().map(result -> SyncResult.builder()
                .status(result.getResult() && result.getValue().getStatus() == 0)
                .code(result.getRawParam().getAccount())
                .message(result.getResult() ? result.getValue().getMessage() : result.getExceptionMessage())
                .build()
        ).collect(Collectors.toList());
    }

    /**
     * 删除用户
     *
     * @param userList 用户实体列表
     * @return 用户同步状态
     */
    @Override
    public List<SyncResult> deleteUsers(List<User> userList) {

        List<String> userAccount = userList.stream().map(User::getAccount).collect(Collectors.toList());
        Map<String, String> userMap = this.getUserMap(User::getId, User::getAccount, user -> userAccount.contains(user.getAccount()));

        List<ExecuteUtil.AsyncResult<String, Response<String>>> resultList = ExecuteUtil.batchAsyncCall(
                new ArrayList<>(userMap.keySet()), executorService,
                (userId) -> authService.deleteUser(userId, AppToken.getToken()));

        return resultList.stream().map(result -> SyncResult.builder()
                .status(result.getResult() && result.getValue().getStatus() == 0)
                .code(userMap.get(result.getRawParam()))
                .message(result.getResult() ? result.getValue().getMessage() : result.getExceptionMessage())
                .build()
        ).collect(Collectors.toList());

    }
}
